package cn.antblog.multi.datasource.aspectJ;

import cn.antblog.multi.datasource.annotation.DataSource;
import cn.antblog.multi.datasource.util.DynamicDataSourceHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @author yangjy
 * @version 1.0
 * @description: 切面类主要用于获取被数据与注解指定的方法，拿到其注解中的属性值，再设置到数据源key设置组件中，方便数据源类获取该key.
 * 需使用@Order设置切面优先级，否则设置无效
 * @date 2022/11/22 11:21
 */
@Aspect
@Order(100)
public class DynamicDataSourceAdviser {
    /**
    @Aspect:作用是把当前类标识为一个切面供容器读取
    @Pointcut：
    Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分，一是表达式，二是方法签名。方法签名必须是
    public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符，因为表达式不直观，因此我们可以通过方法签名的方式为 此表达式命名。
    因此Pointcut中的方法只需要方法签名，而不需要在方法体内编写实际代码。
    @Around：环绕增强，相当于MethodInterceptor
    @AfterReturning：后置增强，相当于AfterReturningAdvice，方法正常退出时执行
    @Before：标识一个前置增强方法，相当于BeforeAdvice的功能，相似功能的还有
    @AfterThrowing：异常抛出增强，相当于ThrowsAdvice
    @After: final增强，不管是抛出异常或者正常退出都会执行
    */
    @Pointcut("@annotation(cn.antblog.multi.datasource.annotation.DataSource)")
    public void pointcut(){}


    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable{
        try {
            //获取方法签名
            MethodSignature methodSignature  = (MethodSignature) point.getSignature();
            //获取被代理的方法对象
            Method targetMethod = methodSignature.getMethod();
            // 获取数据源注解
            DataSource ds = targetMethod.getAnnotation(DataSource.class);
            if(Objects.nonNull(ds)){
                DynamicDataSourceHolder.setDataSource(ds.value());
            }
            return point.proceed();
        }finally {
            DynamicDataSourceHolder.remove();
        }

    }
}
